/********************************************************************************/
/*										*/
/*		Accessing properties for handles of various types		*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*										*/
/*  Licenses and Notices							*/
/*										*/
/*  1. Copyright Licenses:							*/
/*										*/
/*  - Trusted Computing Group (TCG) grants to the user of the source code in	*/
/*    this specification (the "Source Code") a worldwide, irrevocable, 		*/
/*    nonexclusive, royalty free, copyright license to reproduce, create 	*/
/*    derivative works, distribute, display and perform the Source Code and	*/
/*    derivative works thereof, and to grant others the rights granted herein.	*/
/*										*/
/*  - The TCG grants to the user of the other parts of the specification 	*/
/*    (other than the Source Code) the rights to reproduce, distribute, 	*/
/*    display, and perform the specification solely for the purpose of 		*/
/*    developing products based on such documents.				*/
/*										*/
/*  2. Source Code Distribution Conditions:					*/
/*										*/
/*  - Redistributions of Source Code must retain the above copyright licenses, 	*/
/*    this list of conditions and the following disclaimers.			*/
/*										*/
/*  - Redistributions in binary form must reproduce the above copyright 	*/
/*    licenses, this list of conditions	and the following disclaimers in the 	*/
/*    documentation and/or other materials provided with the distribution.	*/
/*										*/
/*  3. Disclaimers:								*/
/*										*/
/*  - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF	*/
/*  LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH	*/
/*  RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES)	*/
/*  THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE.		*/
/*  Contact TCG Administration (admin@trustedcomputinggroup.org) for 		*/
/*  information on specification licensing rights available through TCG 	*/
/*  membership agreements.							*/
/*										*/
/*  - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED 	*/
/*    WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR 	*/
/*    FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR 		*/
/*    NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY 		*/
/*    OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE.		*/
/*										*/
/*  - Without limitation, TCG and its members and licensors disclaim all 	*/
/*    liability, including liability for infringement of any proprietary 	*/
/*    rights, relating to use of information in this specification and to the	*/
/*    implementation of this specification, and TCG disclaims all liability for	*/
/*    cost of procurement of substitute goods or services, lost profits, loss 	*/
/*    of use, loss of data or any incidental, consequential, direct, indirect, 	*/
/*    or special damages, whether under contract, tort, warranty or otherwise, 	*/
/*    arising in any way out of use or reliance upon this specification or any 	*/
/*    information herein.							*/
/*										*/
/*  (c) Copyright IBM Corp. and others, 2016 - 2024				*/
/*										*/
/********************************************************************************/

//** Description
// The functions in this file are used for accessing properties for handles of
// various types. Functions in other files require handles of a specific
// type but the functions in this file allow use of any handle type.

//** Includes

#include "Tpm.h"

//** Functions
//*** EntityGetLoadStatus()
// This function will check that all the handles access loaded entities.
//  Return Type: TPM_RC
//      TPM_RC_HANDLE           handle type does not match
//      TPM_RC_REFERENCE_Hx     entity is not present
//      TPM_RC_HIERARCHY        entity belongs to a disabled hierarchy
//      TPM_RC_OBJECT_MEMORY    handle is an evict object but there is no
//                               space to load it to RAM
TPM_RC
EntityGetLoadStatus(COMMAND* command  // IN/OUT: command parsing structure
)
{
    UINT32 i;
    TPM_RC result = TPM_RC_SUCCESS;
    //
    for(i = 0; i < command->handleNum; i++)
    {
        TPM_HANDLE handle = command->handles[i];
        switch(HandleGetType(handle))
        {
            // For handles associated with hierarchies, the entity is present
            // only if the associated enable is SET.
            case TPM_HT_PERMANENT:
                switch(handle)
                {
                    // First handle non-hierarchy cases
#if VENDOR_PERMANENT_AUTH_ENABLED == YES
                    case VENDOR_PERMANENT_AUTH_HANDLE:
                        if(!gc.ehEnable)
                            result = TPM_RC_HIERARCHY;
                        break;
#endif
                        // PW session handle and lockout handle are always available
                    case TPM_RS_PW:
                        // Need to be careful for lockout. Lockout is always available
                        // for policy checks but not always available when authValue
                        // is being checked.
                    case TPM_RH_LOCKOUT:
                        // Rather than have #ifdefs all over the code,
                        // CASE_ACT_HANDLE is defined in ACT.h. It is 'case TPM_RH_ACT_x:'
                        // FOR_EACH_ACT(CASE_ACT_HANDLE) creates a simple
                        // case TPM_RH_ACT_x: // for each of the implemented ACT.
                        FOR_EACH_ACT(CASE_ACT_HANDLE)
                        break;
                    default:
                        // If the implementation has a manufacturer-specific value
                        // then test for it here. Since this implementation does
                        // not have any, this implementation returns the same failure
                        // that unmarshaling of a bad handle would produce.
                        if(((TPM_RH)handle >= TPM_RH_AUTH_00)
                           && ((TPM_RH)handle <= TPM_RH_AUTH_FF))
                            // if the implementation has a manufacturer-specific value
                            result = TPM_RC_VALUE;
                        else
                            // The handle either refers to a hierarchy or is invalid.
                            result = ValidateHierarchy(handle);
                        break;
                }
                break;
            case TPM_HT_TRANSIENT:
                // For a transient object, check if the handle is associated
                // with a loaded object.
                if(!IsObjectPresent(handle))
                    result = TPM_RC_REFERENCE_H0;
                break;
            case TPM_HT_PERSISTENT:
                // Persistent object
                // Copy the persistent object to RAM and replace the handle with the
                // handle of the assigned slot.  A TPM_RC_OBJECT_MEMORY,
                // TPM_RC_HIERARCHY or TPM_RC_REFERENCE_H0 error may be returned by
                // ObjectLoadEvict()
                result = ObjectLoadEvict(&command->handles[i], command->index);
                break;
            case TPM_HT_HMAC_SESSION:
                // For an HMAC session, see if the session is loaded
                // and if the session in the session slot is actually
                // an HMAC session.
                if(SessionIsLoaded(handle))
                {
                    SESSION* session;
                    session = SessionGet(handle);
                    // Check if the session is a HMAC session
                    if(session->attributes.isPolicy == SET)
                        result = TPM_RC_HANDLE;
                }
                else
                    result = TPM_RC_REFERENCE_H0;
                break;
            case TPM_HT_POLICY_SESSION:
                // For a policy session, see if the session is loaded
                // and if the session in the session slot is actually
                // a policy session.
                if(SessionIsLoaded(handle))
                {
                    SESSION* session;
                    session = SessionGet(handle);
                    // Check if the session is a policy session
                    if(session->attributes.isPolicy == CLEAR)
                        result = TPM_RC_HANDLE;
                }
                else
                    result = TPM_RC_REFERENCE_H0;
                break;
            case TPM_HT_NV_INDEX:
                // For an NV Index, use the TPM-specific routine
                // to search the IN Index space.
                result = NvIndexIsAccessible(handle);
                break;
            case TPM_HT_PCR:
                // Any PCR handle that is unmarshaled successfully referenced
                // a PCR that is defined.
                break;
#if CC_AC_Send
            case TPM_HT_AC:
                // Use the TPM-specific routine to search for the AC
                result = AcIsAccessible(handle);
                break;
#endif
            case TPM_HT_EXTERNAL_NV:
            case TPM_HT_PERMANENT_NV:
                // Not yet supported.
                result = TPM_RC_VALUE;
                break;
            default:
                // Any other handle type is a defect in the unmarshaling code.
                FAIL(FATAL_ERROR_INTERNAL);
                break;
        }
        if(result != TPM_RC_SUCCESS)
        {
            if(result == TPM_RC_REFERENCE_H0)
                result = result + i;
            else
                result = RcSafeAddToResult(result, TPM_RC_H + g_rcIndex[i]);
            break;
        }
    }
    return result;
}

//*** EntityGetAuthValue()
// This function is used to access the 'authValue' associated with a handle.
// This function assumes that the handle references an entity that is accessible
// and the handle is not for a persistent objects. That is EntityGetLoadStatus()
// should have been called. Also, the accessibility of the authValue should have
// been verified by IsAuthValueAvailable().
//
// This function copies the authorization value of the entity to 'auth'.
// Return Type: UINT16
//      count           number of bytes in the authValue with 0's stripped
UINT16
EntityGetAuthValue(TPMI_DH_ENTITY handle,  // IN: handle of entity
                   TPM2B_AUTH*    auth     // OUT: authValue of the entity
)
{
    TPM2B_AUTH* pAuth = NULL;

    auth->t.size      = 0;

    switch(HandleGetType(handle))
    {
        case TPM_HT_PERMANENT:
        {
            switch(HierarchyNormalizeHandle(handle))
            {
                case TPM_RH_OWNER:
                    // ownerAuth for TPM_RH_OWNER
                    pAuth = &gp.ownerAuth;
                    break;
                case TPM_RH_ENDORSEMENT:
                    // endorsementAuth for TPM_RH_ENDORSEMENT
                    pAuth = &gp.endorsementAuth;
                    break;

                    // The ACT use platformAuth for auth
                    FOR_EACH_ACT(CASE_ACT_HANDLE)

                case TPM_RH_PLATFORM:
                    // platformAuth for TPM_RH_PLATFORM
                    pAuth = &gc.platformAuth;
                    break;
                case TPM_RH_LOCKOUT:
                    // lockoutAuth for TPM_RH_LOCKOUT
                    pAuth = &gp.lockoutAuth;
                    break;
                case TPM_RH_NULL:
                    // nullAuth for TPM_RH_NULL. Return 0 directly here
                    return 0;
                    break;
#if VENDOR_PERMANENT_AUTH_ENABLED == YES
                case VENDOR_PERMANENT_AUTH_HANDLE:
                    // vendor authorization value
                    pAuth = &g_platformUniqueAuth;
#endif
                default:
                    // If any other permanent handle is present it is
                    // a code defect.
                    FAIL(FATAL_ERROR_INTERNAL);
                    break;
            }
            break;
        }
        case TPM_HT_TRANSIENT:
            // authValue for an object
            // A persistent object would have been copied into RAM
            // and would have an transient object handle here.
            {
                OBJECT* object;

                object = HandleToObject(handle);
                // special handling if this is a sequence object
                if(ObjectIsSequence(object))
                {
                    pAuth = &((HASH_OBJECT*)object)->auth;
                }
                else
                {
                    // Authorization is available only when the private portion of
                    // the object is loaded.  The check should be made before
                    // this function is called
                    pAssert(object->attributes.publicOnly == CLEAR);
                    pAuth = &object->sensitive.authValue;
                }
            }
            break;
        case TPM_HT_NV_INDEX:
            // authValue for an NV index
            {
                NV_INDEX* nvIndex = NvGetIndexInfo(handle, NULL);
                pAssert(nvIndex != NULL);
                pAuth = &nvIndex->authValue;
            }
            break;
        case TPM_HT_PCR:
            // authValue for PCR
            pAuth = PCRGetAuthValue(handle);
            break;
        default:
            // If any other handle type is present here, then there is a defect
            // in the unmarshaling code.
            FAIL(FATAL_ERROR_INTERNAL);
            break;
    }
    // Copy the authValue
    MemoryCopy2B((TPM2B*)auth, (TPM2B*)pAuth, sizeof(auth->t.buffer));
    MemoryRemoveTrailingZeros(auth);
    return auth->t.size;
}

//*** EntityGetAuthPolicy()
// This function is used to access the 'authPolicy' associated with a handle.
// This function assumes that the handle references an entity that is accessible
// and the handle is not for a persistent objects. That is EntityGetLoadStatus()
// should have been called. Also, the accessibility of the authPolicy should have
// been verified by IsAuthPolicyAvailable().
//
// This function copies the authorization policy of the entity to 'authPolicy'.
//
//  The return value is the hash algorithm for the policy.
TPMI_ALG_HASH
EntityGetAuthPolicy(TPMI_DH_ENTITY handle,     // IN: handle of entity
                    TPM2B_DIGEST*  authPolicy  // OUT: authPolicy of the entity
)
{
    TPMI_ALG_HASH hashAlg = TPM_ALG_NULL;
    authPolicy->t.size    = 0;

    switch(HandleGetType(handle))
    {
        case TPM_HT_PERMANENT:
            switch(HierarchyNormalizeHandle(handle))
            {
                case TPM_RH_OWNER:
                    // ownerPolicy for TPM_RH_OWNER
                    *authPolicy = gp.ownerPolicy;
                    hashAlg     = gp.ownerAlg;
                    break;
                case TPM_RH_ENDORSEMENT:
                    // endorsementPolicy for TPM_RH_ENDORSEMENT
                    *authPolicy = gp.endorsementPolicy;
                    hashAlg     = gp.endorsementAlg;
                    break;
                case TPM_RH_PLATFORM:
                    // platformPolicy for TPM_RH_PLATFORM
                    *authPolicy = gc.platformPolicy;
                    hashAlg     = gc.platformAlg;
                    break;
                case TPM_RH_LOCKOUT:
                    // lockoutPolicy for TPM_RH_LOCKOUT
                    *authPolicy = gp.lockoutPolicy;
                    hashAlg     = gp.lockoutAlg;
                    break;
#define ACT_GET_POLICY(N)                    \
    case TPM_RH_ACT_##N:                     \
        *authPolicy = go.ACT_##N.authPolicy; \
        hashAlg     = go.ACT_##N.hashAlg;    \
        break;
                    // Get the policy for each implemented ACT
                    FOR_EACH_ACT(ACT_GET_POLICY)
                default:
                    hashAlg = TPM_ALG_ERROR;
                    break;
            }
            break;
        case TPM_HT_TRANSIENT:
            // authPolicy for an object
            {
                OBJECT* object = HandleToObject(handle);
                *authPolicy    = object->publicArea.authPolicy;
                hashAlg        = object->publicArea.nameAlg;
            }
            break;
        case TPM_HT_NV_INDEX:
            // authPolicy for a NV index
            {
                NV_INDEX* nvIndex = NvGetIndexInfo(handle, NULL);
                pAssert(nvIndex != 0);
                *authPolicy = nvIndex->publicArea.authPolicy;
                hashAlg     = nvIndex->publicArea.nameAlg;
            }
            break;
        case TPM_HT_PCR:
            // authPolicy for a PCR
            hashAlg = PCRGetAuthPolicy(handle, authPolicy);
            break;
        default:
            // If any other handle type is present it is a code defect.
            FAIL(FATAL_ERROR_INTERNAL);
            break;
    }
    return hashAlg;
}

//*** EntityGetName()
// This function returns the Name associated with a handle.
TPM2B_NAME* EntityGetName(TPMI_DH_ENTITY handle,  // IN: handle of entity
                          TPM2B_NAME*    name     // OUT: name of entity
)
{
    switch(HandleGetType(handle))
    {
        case TPM_HT_TRANSIENT:
        {
            // Name for an object
            OBJECT* object = HandleToObject(handle);
            // an object with no nameAlg has no name
            if(object->publicArea.nameAlg == TPM_ALG_NULL)
                name->b.size = 0;
            else
                *name = object->name;
            break;
        }
        case TPM_HT_NV_INDEX:
            // Name for a NV index
            NvGetNameByIndexHandle(handle, name);
            break;
        default:
            // For all other types, the handle is the Name
            name->t.size = sizeof(TPM_HANDLE);
            UINT32_TO_BYTE_ARRAY(handle, name->t.name);
            break;
    }
    return name;
}

//*** EntityGetHierarchy()
// This function returns the hierarchy handle associated with an entity.
// a) A handle that is a hierarchy handle is associated with itself.
// b) An NV index belongs to TPM_RH_PLATFORM if TPMA_NV_PLATFORMCREATE,
//    is SET, otherwise it belongs to TPM_RH_OWNER
// c) An object handle belongs to its hierarchy.
TPMI_RH_HIERARCHY
EntityGetHierarchy(TPMI_DH_ENTITY handle  // IN :handle of entity
)
{
    TPMI_RH_HIERARCHY hierarchy = TPM_RH_NULL;

    switch(HandleGetType(handle))
    {
        case TPM_HT_PERMANENT:
            // hierarchy for a permanent handle

            if(HierarchyIsFirmwareLimited(handle) || HierarchyIsSvnLimited(handle))
            {
                hierarchy = handle;
                break;
            }

            switch(handle)
            {
                case TPM_RH_PLATFORM:
                case TPM_RH_ENDORSEMENT:
                case TPM_RH_NULL:
                    hierarchy = handle;
                    break;
                // all other permanent handles are associated with the owner
                // hierarchy. (should only be TPM_RH_OWNER and TPM_RH_LOCKOUT)
                default:
                    hierarchy = TPM_RH_OWNER;
                    break;
            }
            break;
        case TPM_HT_NV_INDEX:
            // hierarchy for NV index
            {
                NV_INDEX* nvIndex = NvGetIndexInfo(handle, NULL);
                pAssert(nvIndex != NULL);

                // If only the platform can delete the index, then it is
                // considered to be in the platform hierarchy, otherwise it
                // is in the owner hierarchy.
                if(IS_ATTRIBUTE(
                       nvIndex->publicArea.attributes, TPMA_NV, PLATFORMCREATE))
                    hierarchy = TPM_RH_PLATFORM;
                else
                    hierarchy = TPM_RH_OWNER;
            }
            break;
        case TPM_HT_TRANSIENT:
            // hierarchy for an object
            {
                OBJECT* object;
                object = HandleToObject(handle);
                if(object->attributes.ppsHierarchy)
                {
                    hierarchy = TPM_RH_PLATFORM;
                }
                else if(object->attributes.epsHierarchy)
                {
                    hierarchy = TPM_RH_ENDORSEMENT;
                }
                else if(object->attributes.spsHierarchy)
                {
                    hierarchy = TPM_RH_OWNER;
                }
            }
            break;
        case TPM_HT_PCR:
            hierarchy = TPM_RH_OWNER;
            break;
        default:
            FAIL(FATAL_ERROR_INTERNAL);
            break;
    }
    // this is unreachable but it provides a return value for the default
    // case which makes the complier happy
    return hierarchy;
}
